package com.minimalist.lindi.manager;

import com.minimalist.common.config.mybatis.bo.PageResp;
import com.minimalist.common.entity.lindi.enums.TownEnum;
import com.minimalist.common.entity.lindi.po.LindiIndexChangeRecord;
import com.minimalist.common.entity.lindi.po.LindiPurchaseTownIndex;
import com.minimalist.common.entity.lindi.vo.index.LindiIndexQueryVO;
import com.minimalist.common.entity.lindi.vo.index.LindiIndexVO;
import com.minimalist.lindi.mapper.LindiIndexChangeRecordMapper;
import com.minimalist.lindi.mapper.LindiPurchaseIndexRecordMapper;
import com.minimalist.lindi.mapper.LindiPurchaseTownIndexMapper;
import com.mybatisflex.core.query.QueryWrapper;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 林地指标管理器
 * 负责林地指标的查询、统计和计算功能
 *
 * @author 11987
 */
@Component
public class LindiIndexManager{

    private static final Logger log = LoggerFactory.getLogger(LindiIndexManager.class);

    // ==================== 常量定义 ====================

    /**
     * 默认指标值
     */
    private static final String DEFAULT_INDEX_VALUE = "0";

    /**
     * 可调出指标格式
     */
    private static final String AVAILABLE_INDEX_FORMAT = "(可调出%s亩)";

    // ==================== 依赖注入 ====================

    private final LindiPurchaseIndexRecordMapper purchaseIndexRecordMapper;
    private final LindiPurchaseTownIndexMapper purchaseTownIndexMapper;
    private final LindiIndexChangeRecordMapper indexChangeRecordMapper;

    public LindiIndexManager(LindiPurchaseIndexRecordMapper purchaseIndexRecordMapper,
                             LindiPurchaseTownIndexMapper purchaseTownIndexMapper,
                             LindiIndexChangeRecordMapper indexChangeRecordMapper){
        this.purchaseIndexRecordMapper = purchaseIndexRecordMapper;
        this.purchaseTownIndexMapper = purchaseTownIndexMapper;
        this.indexChangeRecordMapper = indexChangeRecordMapper;
    }

    // ==================== 公共方法 ====================

    /**
     * 查询林地指标
     * 从指标购买记录和指标变更记录中查询每个镇街的购买指标和交易指标
     *
     * @param queryVO 查询条件
     * @return 镇街指标数据列表
     */
    @Transactional(readOnly = true)
    public PageResp<LindiIndexVO> getLindiIndex(LindiIndexQueryVO queryVO){
        String locationName = queryVO.getLocationName();

        // 获取符合条件的镇街列表
        List<String> townNameList = getFilteredTownNameList(locationName);
        if (townNameList.isEmpty()){
            log.info("未找到符合条件的镇街数据，查询条件：{}", locationName);
            return new PageResp<>();
        }

        // 构建指标数据
        Map<String, LindiIndexVO> resultMap = buildIndexDataMap(townNameList, locationName);

        // 过滤结果并返回
        List<LindiIndexVO> resultList = filterAndConvertToList(resultMap, locationName);

        log.debug("查询完成，共找到{}条镇街指标数据", resultList.size());
        return new PageResp<>(resultList, resultList.size());
    }

    /**
     * 获取可交易的镇街名称列表
     * 返回格式为"镇街名称+(可交易亩数)"的记录列表
     * 只返回可交易亩数大于0的镇街
     *
     * @return 可交易镇街名称列表，格式：镇街名称(可交易亩数)
     */
    @Transactional(readOnly = true)
    public List<String> getEnableTradeTownNameList(){
        List<String> townNameList = getAllTownNames();
        Map<String, LindiIndexVO> resultMap = buildIndexDataMap(townNameList, null);

        return resultMap.values().stream()
                .map(this::calculateAvailableIndex)
                .filter(result -> result.getAvailableIndex().compareTo(BigDecimal.ZERO) > 0)
                .map(result -> result.getLocationName() + String.format(AVAILABLE_INDEX_FORMAT, result.getAvailableIndex()))
                .collect(Collectors.toList());
    }

    /**
     * 根据镇街名称获取可交易指标
     *
     * @param locationName 镇街名称
     * @return 可交易指标数量
     */
    @Transactional(readOnly = true)
    public BigDecimal getAvailableIndexByLocationName(String locationName){
        if (StringUtils.isBlank(locationName)){
            log.warn("镇街名称参数为空，无法查询可交易指标");
            return BigDecimal.ZERO;
        }

        if (!isTownExists(locationName)){
            log.info("镇街名称不存在：{}", locationName);
            return BigDecimal.ZERO;
        }

        LindiIndexVO indexVO = buildSingleTownIndexData(locationName);
        AvailableIndexResult result = calculateAvailableIndex(indexVO);

        log.info("获取镇街{}的可交易指标完成，购买指标：{}，交易指标：{}，可交易亩数：{}",
                locationName, result.getPurchaseIndex(), result.getTradeIndex(), result.getAvailableIndex());

        return result.getAvailableIndex();
    }

    /**
     * 获取市储备库指标统计数据
     *
     * @return 包含统计数据的Map，键包括：reserveTotal（总面积）、tradedTotal（已交易面积）、remainingTotal（剩余面积）
     */
    @Transactional(readOnly = true)
    public Map<String, BigDecimal> getReserveStatistics(){
        log.info("开始获取市储备库指标统计数据");

        List<String> townNameList = getAllTownNames();
        Map<String, LindiIndexVO> resultMap = buildIndexDataMap(townNameList, null);

        StatisticsData statistics = calculateStatistics(resultMap);
        Map<String, BigDecimal> result = createStatisticsResult(statistics);

        log.info("市储备库指标统计数据获取完成：总面积={}亩，已交易={}亩，剩余={}亩",
                statistics.reserveTotal, statistics.tradedTotal, statistics.remainingTotal);

        return result;
    }

    /**
     * 根据镇街名称获取可交易指标（别名方法）
     *
     * @param locationName 镇街名称
     * @return 可交易指标数量
     */
    @Transactional(readOnly = true)
    public BigDecimal getEnableTradeIndexByTownName(String locationName){
        return getAvailableIndexByLocationName(locationName);
    }

    /**
     * 获取指标统计信息
     *
     * @param queryVO 查询条件
     * @return 指标统计列表
     */
    @Transactional(readOnly = true)
    public PageResp<LindiIndexVO> getIndexStatistics(LindiIndexQueryVO queryVO){
        return getLindiIndex(queryVO);
    }

    // ==================== 私有辅助方法 ====================

    /**
     * 获取符合条件的镇街名称列表
     *
     * @param locationName 位置名称过滤条件
     * @return 符合条件的镇街名称列表
     */
    private List<String> getFilteredTownNameList(String locationName){
        if (StringUtils.isBlank(locationName)){
            return getAllTownNames();
        }

        return Arrays.stream(TownEnum.values())
                .map(TownEnum::getDesc)
                .filter(name -> name.contains(locationName))
                .collect(Collectors.toList());
    }

    /**
     * 获取所有镇街名称
     *
     * @return 所有镇街名称列表
     */
    private List<String> getAllTownNames(){
        return Arrays.stream(TownEnum.values())
                .map(TownEnum::getDesc)
                .collect(Collectors.toList());
    }

    /**
     * 检查镇街是否存在
     *
     * @param locationName 镇街名称
     * @return 是否存在
     */
    private boolean isTownExists(String locationName){
        return Arrays.stream(TownEnum.values())
                .anyMatch(town -> town.getDesc().equals(locationName));
    }

    /**
     * 构建指标数据映射
     *
     * @param townNameList 镇街名称列表
     * @param locationName 位置名称过滤条件
     * @return 指标数据映射
     */
    private Map<String, LindiIndexVO> buildIndexDataMap(List<String> townNameList, String locationName){
        Map<String, LindiIndexVO> resultMap = initializeResultMap(townNameList);

        // 处理购买指标记录
        processPurchaseRecords(resultMap, townNameList, locationName);

        // 处理指标变更记录
        processChangeRecords(resultMap, townNameList, locationName);

        return resultMap;
    }

    /**
     * 构建单个镇街的指标数据
     *
     * @param locationName 镇街名称
     * @return 指标数据
     */
    private LindiIndexVO buildSingleTownIndexData(String locationName){
        LindiIndexVO indexVO = createEmptyLindiIndexVO(locationName);

        // 处理购买指标记录
        List<LindiPurchaseTownIndex> purchaseTownIndexes = purchaseTownIndexMapper
                .selectListByQuery(QueryWrapper.create().eq(LindiPurchaseTownIndex::getTownName, locationName));

        purchaseTownIndexes.stream()
                .filter(record -> locationName.equals(record.getTownName()))
                .forEach(record -> addPurchaseIndexValue(indexVO, record.getTownIndex()));

        // 处理指标变更记录
        List<LindiIndexChangeRecord> changeRecords = indexChangeRecordMapper
                .selectListByQuery(buildChangeQuery(Collections.singletonList(locationName), locationName));

        changeRecords.stream()
                .filter(record -> locationName.equals(record.getTownName()))
                .forEach(record -> addTradeIndexValue(indexVO, record.getChangeQuantity()));

        return indexVO;
    }

    /**
     * 过滤结果并转换为列表
     *
     * @param resultMap    结果映射
     * @param locationName 位置名称过滤条件
     * @return 过滤后的结果列表
     */
    private List<LindiIndexVO> filterAndConvertToList(Map<String, LindiIndexVO> resultMap, String locationName){
        List<LindiIndexVO> resultList = new ArrayList<>(resultMap.values());

        // 如果有查询参数，进行二次过滤确保数据一致性
        if (StringUtils.isNotBlank(locationName)){
            resultList = resultList.stream()
                    .filter(vo -> vo.getLocationName() != null && vo.getLocationName().contains(locationName))
                    .collect(Collectors.toList());
        }

        return resultList;
    }

    /**
     * 初始化结果映射
     *
     * @param townNameList 镇街名称列表
     * @return 初始化后的结果映射
     */
    private Map<String, LindiIndexVO> initializeResultMap(List<String> townNameList){
        return townNameList.stream()
                .collect(Collectors.toMap(
                        Function.identity(),
                        this::createEmptyLindiIndexVO,
                        (existing, replacement) -> existing,
                        LinkedHashMap::new
                ));
    }

    /**
     * 创建空的林地指标VO对象
     *
     * @param townName 镇街名称
     * @return 林地指标VO对象
     */
    private LindiIndexVO createEmptyLindiIndexVO(String townName){
        LindiIndexVO vo = new LindiIndexVO();
        vo.setLocationName(townName);
        vo.setPurchaseIndex(DEFAULT_INDEX_VALUE);
        vo.setTradeIndex(DEFAULT_INDEX_VALUE);
        return vo;
    }

    /**
     * 处理购买指标记录
     *
     * @param resultMap    结果映射
     * @param townNameList 镇街名称列表
     * @param locationName 查询条件中的镇街名称
     */
    private void processPurchaseRecords(Map<String, LindiIndexVO> resultMap,
                                        List<String> townNameList, String locationName){
        QueryWrapper queryWrapper = buildPurchaseQuery(townNameList, locationName);
        List<LindiPurchaseTownIndex> purchaseTownIndexes = purchaseTownIndexMapper.selectListByQuery(queryWrapper);

        log.debug("查询到{}条购买指标记录", purchaseTownIndexes.size());

        purchaseTownIndexes.stream()
                .filter(record -> resultMap.containsKey(record.getTownName()))
                .forEach(record -> addPurchaseIndexValue(resultMap.get(record.getTownName()), record.getTownIndex()));
    }

    /**
     * 处理指标变更记录
     *
     * @param resultMap    结果映射
     * @param townNameList 镇街名称列表
     * @param locationName 查询条件中的镇街名称
     */
    private void processChangeRecords(Map<String, LindiIndexVO> resultMap,
                                      List<String> townNameList, String locationName){
        QueryWrapper queryWrapper = buildChangeQuery(townNameList, locationName);
        List<LindiIndexChangeRecord> changeRecords = indexChangeRecordMapper.selectListByQuery(queryWrapper);

        log.debug("查询到{}条指标变更记录", changeRecords.size());

        changeRecords.stream()
                .filter(record -> resultMap.containsKey(record.getTownName()))
                .forEach(record -> addTradeIndexValue(resultMap.get(record.getTownName()), record.getChangeQuantity()));
    }

    /**
     * 构建购买指标记录的查询条件
     *
     * @param townNameList 镇街名称列表
     * @param locationName 查询条件中的镇街名称
     * @return 查询条件
     */
    private QueryWrapper buildPurchaseQuery(List<String> townNameList, String locationName){
        QueryWrapper queryWrapper = QueryWrapper.create();

        // 只有当locationName不为空时才添加like条件，避免不必要的模糊查询
        if (StringUtils.isNotBlank(locationName)){
            queryWrapper.like(LindiPurchaseTownIndex::getTownName, locationName);
        }

        // 添加in条件，限定查询范围
        if (!townNameList.isEmpty()){
            queryWrapper.in(LindiPurchaseTownIndex::getTownName, townNameList);
        }

        return queryWrapper;
    }

    /**
     * 构建指标变更记录的查询条件
     *
     * @param townNameList 镇街名称列表
     * @param locationName 查询条件中的镇街名称
     * @return 查询条件
     */
    private QueryWrapper buildChangeQuery(List<String> townNameList, String locationName){
        QueryWrapper queryWrapper = QueryWrapper.create();

        // 只有当locationName不为空时才添加like条件，避免不必要的模糊查询
        if (StringUtils.isNotBlank(locationName)){
            queryWrapper.like(LindiIndexChangeRecord::getTownName, locationName);
        }

        // 添加in条件，限定查询范围
        if (!townNameList.isEmpty()){
            queryWrapper.in(LindiIndexChangeRecord::getTownName, townNameList);
        }

        return queryWrapper;
    }

    /**
     * 添加购买指标值
     *
     * @param indexVO       指标VO对象
     * @param quantityToAdd 要累加的数量
     */
    private void addPurchaseIndexValue(LindiIndexVO indexVO, String quantityToAdd){
        addIndexValue(indexVO, quantityToAdd, indexVO::getPurchaseIndex, indexVO::setPurchaseIndex, "购买指标");
    }

    /**
     * 添加交易指标值
     *
     * @param indexVO       指标VO对象
     * @param quantityToAdd 要累加的数量
     */
    private void addTradeIndexValue(LindiIndexVO indexVO, String quantityToAdd){
        addIndexValue(indexVO, quantityToAdd, indexVO::getTradeIndex, indexVO::setTradeIndex, "交易指标");
    }

    /**
     * 累加指标值的通用方法
     *
     * @param indexVO       指标VO对象
     * @param quantityToAdd 要累加的数量
     * @param getter        获取当前值的方法
     * @param setter        设置新值的方法
     * @param indexType     指标类型（用于日志）
     */
    private void addIndexValue(LindiIndexVO indexVO, String quantityToAdd,
                               Supplier<String> getter, Consumer<String> setter, String indexType){
        if (StringUtils.isBlank(quantityToAdd)){
            log.warn("{}数值为空，镇街名称：{}，当前值：{}，记录值：{}",
                    indexType, indexVO.getLocationName(), getter.get(), quantityToAdd);
            return;
        }

        try{
            BigDecimal current = new BigDecimal(getter.get());
            BigDecimal toAdd = new BigDecimal(quantityToAdd);
            BigDecimal result = current.add(toAdd);
            setter.accept(result.toString());
        } catch (NumberFormatException e){
            log.warn("{}数值格式异常，镇街名称：{}，当前值：{}，记录值：{}，异常信息：{}",
                    indexType, indexVO.getLocationName(), getter.get(), quantityToAdd, e.getMessage());
        }
    }

    /**
     * 计算可交易指标
     *
     * @param indexVO 指标VO对象
     * @return 可交易指标计算结果
     */
    private AvailableIndexResult calculateAvailableIndex(LindiIndexVO indexVO){
        try{
            BigDecimal purchaseIndex = parseIndexValue(indexVO.getPurchaseIndex());
            BigDecimal tradeIndex = parseIndexValue(indexVO.getTradeIndex());
            BigDecimal availableIndex = purchaseIndex.subtract(tradeIndex);

            // 如果可交易亩数小于0，返回0
            if (availableIndex.compareTo(BigDecimal.ZERO) < 0){
                availableIndex = BigDecimal.ZERO;
            }

            return new AvailableIndexResult(indexVO.getLocationName(), purchaseIndex, tradeIndex, availableIndex);
        } catch (NumberFormatException e){
            log.warn("计算可交易亩数异常，镇街名称：{}，购买指标：{}，交易指标：{}，异常信息：{}",
                    indexVO.getLocationName(), indexVO.getPurchaseIndex(), indexVO.getTradeIndex(), e.getMessage());
            return new AvailableIndexResult(indexVO.getLocationName(), BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO);
        }
    }

    /**
     * 计算统计数据
     *
     * @param resultMap 结果映射
     * @return 统计数据
     */
    private StatisticsData calculateStatistics(Map<String, LindiIndexVO> resultMap){
        BigDecimal reserveTotal = BigDecimal.ZERO;
        BigDecimal tradedTotal = BigDecimal.ZERO;

        for (LindiIndexVO indexVO : resultMap.values()){
            try{
                reserveTotal = reserveTotal.add(parseIndexValue(indexVO.getPurchaseIndex()));
                tradedTotal = tradedTotal.add(parseIndexValue(indexVO.getTradeIndex()));
            } catch (NumberFormatException e){
                log.warn("统计数据计算异常，镇街名称：{}，购买指标：{}，交易指标：{}，异常信息：{}",
                        indexVO.getLocationName(), indexVO.getPurchaseIndex(),
                        indexVO.getTradeIndex(), e.getMessage());
            }
        }

        BigDecimal remainingTotal = reserveTotal.subtract(tradedTotal);
        if (remainingTotal.compareTo(BigDecimal.ZERO) < 0){
            remainingTotal = BigDecimal.ZERO;
        }

        return new StatisticsData(reserveTotal, tradedTotal, remainingTotal);
    }

    /**
     * 解析指标值
     *
     * @param indexValue 指标值字符串
     * @return 解析后的BigDecimal值
     */
    private BigDecimal parseIndexValue(String indexValue){
        if (StringUtils.isBlank(indexValue)){
            return BigDecimal.ZERO;
        }
        return new BigDecimal(indexValue);
    }

    /**
     * 创建统计结果映射
     *
     * @param statistics 统计数据
     * @return 统计结果映射
     */
    private Map<String, BigDecimal> createStatisticsResult(StatisticsData statistics){
        Map<String, BigDecimal> result = new LinkedHashMap<>();
        result.put("reserveTotal", statistics.reserveTotal);
        result.put("tradedTotal", statistics.tradedTotal);
        result.put("remainingTotal", statistics.remainingTotal);
        return result;
    }

    // ==================== 内部类 ====================

    /**
     * 统计数据内部类
     */
    private static class StatisticsData{
        final BigDecimal reserveTotal;
        final BigDecimal tradedTotal;
        final BigDecimal remainingTotal;

        StatisticsData(BigDecimal reserveTotal, BigDecimal tradedTotal, BigDecimal remainingTotal){
            this.reserveTotal = reserveTotal;
            this.tradedTotal = tradedTotal;
            this.remainingTotal = remainingTotal;
        }
    }

    /**
     * 可交易指标计算结果内部类
     */
    private static class AvailableIndexResult{
        private final String locationName;
        private final BigDecimal purchaseIndex;
        private final BigDecimal tradeIndex;
        private final BigDecimal availableIndex;

        AvailableIndexResult(String locationName, BigDecimal purchaseIndex, BigDecimal tradeIndex, BigDecimal availableIndex){
            this.locationName = locationName;
            this.purchaseIndex = purchaseIndex;
            this.tradeIndex = tradeIndex;
            this.availableIndex = availableIndex;
        }

        public String getLocationName(){
            return locationName;
        }

        public BigDecimal getPurchaseIndex(){
            return purchaseIndex;
        }

        public BigDecimal getTradeIndex(){
            return tradeIndex;
        }

        public BigDecimal getAvailableIndex(){
            return availableIndex;
        }
    }
}